﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Helper.FullTextIndex;
using TI_EntityLib.TiEntityObjects.AttachInfo;
using MergeTI.TiEntityObjects.Clifar;
using TI_EntityLib.TiEntityObjects.Clifar;
using System.Collections;

namespace TI.TI_DruginfoAnalizer
{


    /// <summary>
    /// Анализатор информации о таблетках.
    /// Индексирует в память эталонный поисковый справочник по таблеткам.
    /// Производит по справчнику полнотектовый поиск - с выводом ранжированного списка 
    /// Однозначно определение значение 
    /// </summary>
    public class DrugInfoAnalizer
    {
        #region Tools classes
        /// <summary>
        /// Транспортная единичка
        /// </summary>
        public class DrugInfoDataItem : IFullindexObject
        {
            private string _ID = Guid.NewGuid().ToString();
            
            public string ID
            {
                get { return _ID; }
                set { _ID = value; }
            }

            public string ValueInfo
            {
                get;
                set;
            }

            public object Owner { get; set; }


            public List<string> Terms { get { return RamEngine.ParseText(ValueInfo); } }

            public int LengthRank
            {
                get { return Terms.Count; }
            }

            public override string ToString()
            {
                return Helper.Json<DrugInfoDataItem>.NewtonSerializeObject(this);                
            }


        }

        /// <summary>
        /// Элемент ранжирования
        /// </summary>
        public class RankIFullindexObject
        {
            public int Rank { get; set; }
            public IFullindexObject FullindexObject { get; set; }
        }
        #endregion
        
        static List<IFullindexObject> _etalon = new List<IFullindexObject>();        
        static Helper.FullTextIndex.RamEngine _etalonEngine = null;

        //Эталон в виде TextItem
        static Hashtable __etalon = new Hashtable();  

        /// <summary>
        /// Эталонный справочник по таблеткам db.TradeNmTree
        /// </summary>
        public static List<IFullindexObject> GetEtalon()
        {
            var script = "db.DrugInfoMnnDictionary.find().toArray();";
            List<MnnTradeNmDictionaryRecord > lst = Helper.MongoTools.Instance.GetCollection<MnnTradeNmDictionaryRecord >(script);

            __etalon = new Hashtable();
            //Формируем варианты написания 
            List<DrugInfoDataItem> _result = new List<DrugInfoDataItem>();

            //функция занимающаяся проверкой TradeNmTreeItem и формированием List<TradeNmTreeItem> 
            Func<MnnTradeNmDictionaryRecord , bool> f = delegate(MnnTradeNmDictionaryRecord  i)
            {
                //InnR
                DrugInfoDataItem c = null;
                if (i.InnR.Length > 3 && !__etalon.ContainsKey(i.InnR))
                {
                    c = new DrugInfoDataItem() { ID = i.ID, ValueInfo = i.InnR, Owner = i };
                    _result.Add(c);
                    __etalon.Add(c.ValueInfo, c);
                }
                //InnE
                if (i.InnE != null && i.InnE.Length > 3 && !__etalon.ContainsKey(i.InnE))
                {
                    c = new DrugInfoDataItem() { ID = i.ID, ValueInfo = i.InnE, Owner = i };
                    _result.Add(c);
                    __etalon.Add(c.ValueInfo, c);
                }
                //TradeNmR
                if (i.TradeNmR!= null && i.TradeNmR.Length > 3 && !__etalon.ContainsKey(i.TradeNmR))
                {
                    c = new DrugInfoDataItem() { ID = i.ID, ValueInfo = i.TradeNmR, Owner = i };
                    _result.Add(c);
                    __etalon.Add(c.ValueInfo, c);
                }

                //DrugFmNmR 
                if (i.TradeNmE != null && i.TradeNmE.Length > 3 && !__etalon.ContainsKey(i.TradeNmE))
                {
                    c = new DrugInfoDataItem() { ID = i.ID, ValueInfo = i.TradeNmE, Owner = i };
                    _result.Add(c);
                    __etalon.Add(c.ValueInfo, c);
                }


                return true;
            };

            foreach (MnnTradeNmDictionaryRecord i in lst) { f(i); }

            return _result.ToList<IFullindexObject>();
        } 
        
        /// <summary>
        /// Обратиться к БД сформировать поисковый индекс
        /// </summary>
        public DrugInfoAnalizer() {         
            RebuildEtalon();
        }
        
        /// <summary>
        /// Запрос по базе эталонного справочника и его переиндексация
        /// </summary>
        public void RebuildEtalon() {

            _etalon = GetEtalon();
            _etalonEngine = new RamEngine();
            _etalonEngine.IndexCollection(_etalon);

        }

        /// <summary>
        /// Поисковый запрос с заданным количеством результатов. 
        /// </summary>
        /// <param name="mathString"></param>
        /// <param name="maxResultLength"></param>        
        /// <returns></returns>        
        public List<IFullindexObject> SearchApplicatians(string mathString,  int maxResultLength)
        {
            List<IFullindexObject> result = new List<IFullindexObject>();
            mathString = RamEngine.ClearForbiddenChar(mathString);
            
            if (string.IsNullOrEmpty(mathString))
                                        return result;

            List<SearchResult> applicants = _etalonEngine.SearchByField("ValueInfo", "ID", mathString );

            //кандидаты отсутствуют 
            if (applicants.Count == 0)
                                return result;
            // первые maxResultLength
            if (applicants.Count < maxResultLength)
                        return applicants.ToList <IFullindexObject>() ;
            
            
            List<IFullindexObject> arr = new List<IFullindexObject>();
            for(int i= 0;i<maxResultLength ;i++)
            {
                arr.Add(applicants[i]);
            }

            return arr;
        }
        /// <summary>
        /// маппинг эталонных значений на результаты поиска
        /// </summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        public List<DrugInfoDataItem> FullindexItemToTextItem(List<IFullindexObject> arr)
        {
            List<DrugInfoDataItem> result = new List<DrugInfoDataItem>();
            foreach (IFullindexObject i in arr) {
                var r = __etalon[i.ValueInfo];
                result.Add((DrugInfoDataItem)r); 
            }
            return result;

        }

        public DrugInfoDataItem FullindexItemToTextItem(IFullindexObject item)
        {
            return (DrugInfoDataItem)__etalon[item.ValueInfo];            
       }
       
        /// <summary>
        /// Однозначное определение кандидата по эталонным данным. 
        /// Возвращается единственное значение либо null
        /// </summary>
        /// <param name="mathString"></param>
        /// <param name="accuracy"></param>
        /// <returns></returns>
        public  IFullindexObject SearchApplicatian(string mathString)
        {

            string _mathString = string.Empty;
            
            //кандидаты из справочника
            List<IFullindexObject> applicaians = SearchApplicatians(RamEngine.ClearForbiddenChar(mathString), 25);
            
            //перебор кандидатов

            //формируем индекс из поисковой строки 
            Helper.FullTextIndex.RamEngine engine = new RamEngine();
            List<IFullindexObject> textList = new List<IFullindexObject>();
            textList.Add(new DrugInfoDataItem() { ValueInfo = mathString });
            engine.IndexCollection(textList);



            List<IFullindexObject> result = new List<IFullindexObject>();
            //отобрать из кандидатов однозначно подходящие (все слова кандидата встречаются в mathString)
            List<IFullindexObject> shortListApplicaians = new List<IFullindexObject>();
            foreach (IFullindexObject i in applicaians)
            {
                //почистить спецсимволы
                _mathString = RamEngine.ClearForbiddenChar(i.ValueInfo);
                
                //сформировать требование наличеи каждого слова
                _mathString = RamEngine.BuildCompulsoryQuery(_mathString);
                
                //сравнение
                result = engine.SearchByField("ValueInfo", "ID", _mathString).ToList<IFullindexObject>();
                //кандидат не прошел
                if (null == result)
                    continue;
                //кандидат однозначно подошел
                if (result.Count == 1)
                    shortListApplicaians.Add(i);

            }
            //шорт лист пуст ?
            if (shortListApplicaians.Count == 0) {
                return null;
            }

            //оценить список по длине (оставить только макимально длинных)            
            var _select = from n in shortListApplicaians
                where n.LengthRank == shortListApplicaians.Max(a => a.LengthRank)
                select n;
            List<IFullindexObject> _shortList= _select.ToList<IFullindexObject>();
            
            //если есть однозначность
            if (_shortList.Count == 1)
                        return _shortList[0];

            //Даем оценку каждому элименту на предмет похожести
            List<RankIFullindexObject> __shortList = new List<RankIFullindexObject>();
            List<string> gg = new List<string>();
            foreach (IFullindexObject i in _shortList)
            {
                int cost = Helper.TextTools.GetValueOfStringTransform(i.ValueInfo, mathString, ref gg);
                RankIFullindexObject obj = new RankIFullindexObject() { FullindexObject = i, Rank = cost };
                __shortList.Add(obj);
            }


            //оценить список по похожести (оставить только минимально дешевых)            
            var __select = from n in __shortList
                          where n.Rank == __shortList.Min(a => a.Rank)
                          select n;

            List<RankIFullindexObject> ___shortList = __select.ToList<RankIFullindexObject>();

            //если есть однозначность
            if (___shortList.Count == 1)
                    return ___shortList[0].FullindexObject;

            //искали скали не нашли
            return null;
        }
    }
}

